package controllers

import (
	"gouniapp/configs"
	"database/sql"
	"fmt"
	"net"
	"net/http"
	"regexp"
	"strconv"
	"golang.org/x/sync/singleflight"
	"encoding/json"
    "path/filepath"
	"log"
	"time"
	"strings"
	"bytes"
	"os"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
)

// 定义配置结构体
type Myconfig struct {
	Server struct {
		Port        int      `json:"port"`
		URL         string   `json:"url"`
		Name        string   `json:"name"`
		Shuiyin     string   `json:"shuiyin"`
		CorsOrigins string  `json:"corsOrigins"`
		SN          string   `json:"sn"`
		Footer      string   `json:"footer"`
		Jfsc        string   `json:"jfsc"`
		Reg         string   `json:"reg"`
	} `json:"server"`
	MySQL struct {
		Username  string `json:"username"`
		Password  string `json:"password"`
		Host      string `json:"host"`
		Port      int    `json:"port"`
		Database  string `json:"database"`
	} `json:"mysql"`
	Wechat struct {
		AppID  string `json:"appid"`
		Secret string `json:"secret"`
	} `json:"wechat"`
	Baidu struct {
		BaiduAPIKey    string `json:"BaiduAPIKey"`
		BaiduSecretKey string `json:"BaiduSecretKey"`
	} `json:"baidu"`
	AI struct {
		APIKey      string `json:"apikey"`
		Model       string `json:"model"`
		Ocrurl       string `json:"ocrurl"`
	} `json:"ai"`
	Fonts map[string]string `json:"fonts"`
}

// 获取系统配置
func Getconfig(c *gin.Context) {
	myconfig := Myconfig{}
	if err := configs.Config.Unmarshal(&myconfig); err != nil {
		JsonResponse(c, http.StatusInternalServerError, "配置解析失败", err.Error())
		return
	}
	JsonResponse(c, http.StatusOK, "获取成功", myconfig)
}

// 保存配置
func Saveconfig(c *gin.Context) {
	var myconfig Myconfig
	if err := c.ShouldBindJSON(&myconfig); err != nil {
		JsonResponse(c, http.StatusBadRequest, "无效的请求数据", err.Error())
		return
	}
	if configs.MyURL == "https://zp.kzhgzs.cn/" {
	    JsonResponse(c, http.StatusBadRequest, "演示站禁止修改", nil)
		return
	}
	// 验证配置
	errorMessages := validateConfig(myconfig)
    if len(errorMessages) > 0 {
        JsonResponse(c, http.StatusBadRequest, "验证失败", strings.Join(errorMessages, "; "))
        return
    }
	// 将结构体转换为 map[string]interface{}
	newConfig := map[string]interface{}{
		"server": map[string]interface{}{
			"port":        myconfig.Server.Port,
			"url":         myconfig.Server.URL,
			"name":        myconfig.Server.Name,
			"shuiyin":     myconfig.Server.Shuiyin,
			"corsOrigins": myconfig.Server.CorsOrigins,
			"sn":          myconfig.Server.SN,
			"footer":      myconfig.Server.Footer,
			"jfsc":        myconfig.Server.Jfsc,
			"reg":         myconfig.Server.Reg,
		},
		"mysql": map[string]interface{}{
			"username":  myconfig.MySQL.Username,
			"password":  myconfig.MySQL.Password,
			"host":      myconfig.MySQL.Host,
			"port":      myconfig.MySQL.Port,
			"database":  myconfig.MySQL.Database,
		},
		"wechat": map[string]interface{}{
			"appid":  myconfig.Wechat.AppID,
			"secret": myconfig.Wechat.Secret,
		},
		"baidu": map[string]interface{}{
			"BaiduAPIKey":    myconfig.Baidu.BaiduAPIKey,
			"BaiduSecretKey": myconfig.Baidu.BaiduSecretKey,
		},
		"AI": map[string]interface{}{
			"apikey":   myconfig.AI.APIKey,
			"model":    myconfig.AI.Model,
			"ocrurl":   myconfig.AI.Ocrurl,
		},
		"fonts": myconfig.Fonts,
	}
	// 更新配置
	if err := UpdateFullConfig(newConfig); err != nil {
		JsonResponse(c, http.StatusInternalServerError, "配置更新失败", err.Error())
		return
	}
	JsonResponse(c, http.StatusOK, "配置保存成功", myconfig)
}

// 验证配置
func validateConfig(myconfig Myconfig) []string {
	var errorMessages []string
	// 验证server.port
	if myconfig.Server.Port == 0 {
		errorMessages = append(errorMessages, "端口不能为空")
	} else if myconfig.Server.Port < 80 || myconfig.Server.Port > 6000 {
		errorMessages = append(errorMessages, "端口范围80-6000")
	}
	// 验证server.url
	if myconfig.Server.URL == "" {
		errorMessages = append(errorMessages, "服务地址不能为空")
	} else {
		matched, _ := regexp.MatchString(`^https?://[^\s]+/$`, myconfig.Server.URL)
		if !matched {
			errorMessages = append(errorMessages, "请输入以http://或https://开头，结尾有斜杠的有效URL地址")
		}
	}
	// 验证mysql
	if myconfig.MySQL.Host == "" {
		errorMessages = append(errorMessages, "数据库地址不能为空")
	}
	if myconfig.MySQL.Username == "" {
		errorMessages = append(errorMessages, "数据库用户名不能为空")
	}
	if myconfig.MySQL.Password == "" {
		errorMessages = append(errorMessages, "数据库密码不能为空")
	}
	if myconfig.MySQL.Database == "" {
		errorMessages = append(errorMessages, "数据库名称不能为空")
	}
	if myconfig.MySQL.Port < 1024 || myconfig.MySQL.Port > 65535 {
		errorMessages = append(errorMessages, "MySQL端口范围1024-65535")
	}
	// 验证MySQL连接
	if errMsg := checkMySQL(myconfig.MySQL.Username, myconfig.MySQL.Password, myconfig.MySQL.Host,
		strconv.Itoa(myconfig.MySQL.Port), myconfig.MySQL.Database); errMsg != "" {
		errorMessages = append(errorMessages, errMsg)
	}
	// 验证端口可用性
	if !checkPort(myconfig.Server.Port) {
		errorMessages = append(errorMessages, fmt.Sprintf("端口%d已被占用或不可用", myconfig.Server.Port))
	}
	// 验证序列号有效性
	if !checksn(myconfig.Server.SN) {
		errorMessages = append(errorMessages, "序列号无效")
	}
    for fontName, fontPath := range myconfig.Fonts {
		if fontName == "" {
			errorMessages = append(errorMessages, "字体名称不能为空")
		}
		if fontPath == "" {
			errorMessages = append(errorMessages, fmt.Sprintf("字体 '%s' 的文件路径不能为空", fontName))
		}
	}
	return errorMessages
}

// 检测MySQL连接配置
func checkMySQL(username, password, dbhost, dbport, database string) string {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",username, password, dbhost, dbport, database)
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return "数据库连接配置错误"
	}
	defer db.Close()
	if err := db.Ping(); err != nil {
		return "数据库连接验证失败"
	}
	return ""
}

// 检测端口可用性（排除当前正在使用的端口）
func checkPort(newPort int) bool {
	currentPort := configs.Config.GetInt("server.port")
	if newPort == currentPort {
		return true // 允许使用当前端口
	}
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", newPort))
	if err != nil {
		return false
	}
	listener.Close()
	return true
}

// 检查序列号有效性
func checksn(serialNumber string) bool {
    currentHardwareID := configs.Getsysid()
    return configs.ValidateSerialNumber(serialNumber, currentHardwareID)
}

// 更新完整配置
func UpdateFullConfig(newConfig map[string]interface{}) error {
	if server, ok := newConfig["server"].(map[string]interface{}); ok {
		if url, ok := server["url"].(string); ok {
			server["url"] = processURL(url)
		}
	}
	// 批量更新配置
	for section, values := range newConfig {
		configs.Config.Set(section, values)
	}
	// 持久化到配置文件
	if err := configs.Config.WriteConfig(); err != nil {
		return fmt.Errorf("配置持久化失败: %w", err)
	}
	// 更新JS文件中的URL配置
	configs.Writejs(configs.Config.GetString("server.url"))
	return nil
}

// 标准化URL格式
func processURL(rawURL string) string {
	url := strings.TrimSpace(rawURL)
	if !strings.HasSuffix(url, "/") {
		url += "/"
	}
	return url
}

// 添加singleflight组防止并发写入
var menuSaveGroup singleflight.Group
const jsTemplate = `window.menus = %s;`  // 使用%s直接格式化字符串

// pc端菜单
type MenuItem struct {
	Path     string     `json:"path"`
	Name     string     `json:"name"`
	Icon     string     `json:"icon"`
	FullPath string     `json:"fullPath"`
	Visible  bool       `json:"visible"`
	Roles   []string    `json:"roles"` //角色可见
	Children []MenuItem `json:"children"`
}

// 保存菜单
func SaveMenuHandler(c *gin.Context) {
	var menus []MenuItem
	if err := c.ShouldBindJSON(&menus); err != nil {
		log.Printf("[JSON] 绑定失败: %v", err)
		JsonResponse(c, http.StatusBadRequest, "无效数据格式", nil)
		return
	}

	// 数据校验
	if err := validateMenus(menus); err != nil {
		log.Printf("[校验] 数据不合法: %v", err)
		JsonResponse(c, http.StatusBadRequest, err.Error(), nil)
		return
	}
	
    key := "save_menu"
    _, err, _ := menuSaveGroup.Do(key, func() (interface{}, error) {
        // 生成无转义JSON
        buf := new(bytes.Buffer)
        enc := json.NewEncoder(buf)
        enc.SetEscapeHTML(false)
        enc.SetIndent("", "  ")
        if err := enc.Encode(menus); err != nil {
            return nil, fmt.Errorf("生成配置失败")
        }
        jsArray := strings.TrimSpace(buf.String())
        // 直接拼接文件内容（关键修改）
        fileContent := fmt.Sprintf(jsTemplate, jsArray)
        // 原子写入文件
        filePath := filepath.Join("templates", "pc", "static", "menus.js")
        if err := atomicWriteFile(filePath, []byte(fileContent)); err != nil {
            return nil, err
        }
        return nil, nil
    })
	if err != nil {
		log.Printf("[系统] 保存失败: %v", err)
		JsonResponse(c, http.StatusInternalServerError, err.Error(), nil)
		return
	}
	JsonResponse(c, http.StatusOK, "保存成功", gin.H{
		"update_time": time.Now().Format(time.RFC3339),
	})
}

// 原子写入文件（防止写入中途失败）
func atomicWriteFile(path string, data []byte) error {
	// 创建临时文件
	tmpFile := path + ".tmp"
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		return fmt.Errorf("目录创建失败: %w", err)
	}
	// 写入临时文件
	if err := os.WriteFile(tmpFile, data, 0644); err != nil {
		return fmt.Errorf("临时文件写入失败: %w", err)
	}
	// 重命名替换原文件
	if err := os.Rename(tmpFile, path); err != nil {
		return fmt.Errorf("文件替换失败: %w", err)
	}
	return nil
}

// 数据校验逻辑
func validateMenus(menus []MenuItem) error {
	pathRegex := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
	nameRegex := regexp.MustCompile(`^[\p{Han}a-zA-Z0-9_-]{2,20}$`) // 支持中文
	roleRegex := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) // 角色正则
	for i, menu := range menus {
		// 校验路径
		if menu.Path == "" {
			return fmt.Errorf("第%d行: path不能为空", i+1)
		}
		if !pathRegex.MatchString(menu.Path) {
			return fmt.Errorf("第%d行: path只能包含字母、数字、下划线和短横线", i+1)
		}
		// 校验名称
		if menu.Name == "" {
			return fmt.Errorf("第%d行: name不能为空", i+1)
		}
		if !nameRegex.MatchString(menu.Name) {
			return fmt.Errorf("第%d行: 名称需2-20位，支持中文、英文、数字和下划线", i+1)
		}
        // 校验Roles非空且合法
        if len(menu.Roles) == 0 {
            return fmt.Errorf("第%d行: 至少需要指定一个角色", i+1)
        }
        for _, role := range menu.Roles {
            if !roleRegex.MatchString(role) {
                return fmt.Errorf("第%d行: 角色 '%s' 包含非法字符", i+1, role)
            }
        }
		// 校验子菜单（递归校验）
		if len(menu.Children) > 0 {
			if err := validateMenus(menu.Children); err != nil {
				return fmt.Errorf("第%d行子菜单: %v", i+1, err)
			}
		}
	}
	return nil
}